﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Xml;
using System.Xml.Linq;
using TciNewsReader_2014_04.Interfaces;
namespace TciNewsReader_2014_04.Model
{
    public sealed class FeedInfo : INotifyPropertyChanged
    {
        private SyndicationFeed _feed;
        private bool _copyOfIncludeInViewForRestore;
        private static readonly UriHelpers UriHelpers = new UriHelpers();
        public List<Exception> Exceptions { get; private set; }
        public List<ItemWrapper> Items { get; private set; }
        public int FilteredCount { get { return Items != null ? Items.Count(q => q.FilterStatus == FilterStatus.FilteredOut) : 0; } }
        public int ItemsCount { get { return Items != null ? Items.Count : 0; } }
        public int VisibleCount { get { return Items != null ? Items.Count(q => q.FilterStatus == FilterStatus.FilteredIn) : 0; } }
        public FeedInfo(string url)
        {
            Url = url;
            IncludeInView = Infrastructure.Constants.DefaultFeedInclusion;
            Items = new List<ItemWrapper>();
            Exceptions = new List<Exception>();
        }
        public string Host
        {
            get
            {
                string valResult;
                if (UriHelpers.TryValidateUri(Url, out valResult))
                {
                    Uri didCreate;
                    if (Uri.TryCreate(Url, UriKind.Absolute, out didCreate))
                    {
                        var s = didCreate.Host;
                        return s;
                    }
                }
                return "Unknown";
            }
        }
        private bool _wrappersHaveBeenProcessed;
        public bool WrappersHaveBeenProcessed
        {
            [DebuggerStepThrough]
            get { return _wrappersHaveBeenProcessed; }
            [DebuggerStepThrough]
            set
            {
                if (value != _wrappersHaveBeenProcessed)
                {
                    _wrappersHaveBeenProcessed = value;
                    OnPropertyChanged("WrappersHaveBeenProcessed");
                }
            }
        }
        private bool _includeInView;
        public bool IncludeInView
        {
            [DebuggerStepThrough] get { return _includeInView; }
            [DebuggerStepThrough]
            set
            {
                if (value != _includeInView)
                {
                    _includeInView = value;
                    OnPropertyChanged("IncludeInView");
                }
            }
        }
        private string _url;
        public string Url
        {
            [DebuggerStepThrough] get { return _url; }
            [DebuggerStepThrough]
            set
            {
                if (value != _url)
                {
                    _url = value;
                    OnPropertyChanged("Url");
                }
            }
        }
        public string FeedPublisherName
        {
            get { return _feed == null ? "" : _feed.Title == null ? Url : _feed.Title.Text; }
        }
        public void Load(IExceptionHandler exceptionHandler, Action<FeedInfo>onComplete)
        {
            try
            {
                Items.Clear();
                Exceptions.Clear();
                string validationResult;
                if (UriHelpers.TryValidateUri(Url, out validationResult))
                {
                    using (XmlReader reader = XmlReader.Create(Url, new XmlReaderSettings {DtdProcessing = DtdProcessing.Parse, ValidationType = ValidationType.None}))
                    {
                        try
                        {
                            _feed = SyndicationFeed.Load(reader);
                            if (_feed != null) Items = _feed.Items.Select(q => new ItemWrapper(q, this, FeedPublisherName)).ToList();
                        }
                        catch (Exception ex)
                        {
                            Exceptions.Add(ex);
                            exceptionHandler.HandleException(ex, Url);
                            FallbackParser(Url, exceptionHandler);
                        }
                    }
                }
                else
                {
                    var validationException = new ApplicationException(validationResult);
                    Exceptions.Add(validationException);
                    exceptionHandler.HandleException(validationException, Url);
                }
            }
            catch (Exception ex)
            {
                Exceptions.Add(ex);
                exceptionHandler.HandleException(ex, Url);
            }
            foreach (ItemWrapper iw in Items)
            {
                ItemWrapperHistoryManager.SetStatus(iw);
            } 
            if (onComplete != null)
            {
                onComplete(this);
            }
        }
        private void FallbackParser(string url, IExceptionHandler ehandler)
        {
            try
            {
                if (_feed == null) _feed = new SyndicationFeed();
                using (XmlReader reader = XmlReader.Create(url, new XmlReaderSettings {DtdProcessing = DtdProcessing.Parse, ValidationType = ValidationType.None}))
                {
                    XDocument doc = XDocument.Load(reader);
                    XElement title = doc.Elements("rss").Elements("channel").Elements("title").FirstOrDefault();
                    _feed.Title = title != null ? new TextSyndicationContent(title.Value) : new TextSyndicationContent(String.Empty);
                    IEnumerable<XElement> items = doc.Elements("rss").Elements("channel").Elements("item");
                    foreach (XElement item in items)
                    {
                        try
                        {
                            XElement titleElement = item.Element("title");
                            if (titleElement == null) continue;
                            XElement descriptionElement = item.Element("description");
                            if (descriptionElement == null) continue;
                            XElement linkElement = item.Element("link");
                            if (linkElement == null) continue;
                            SyndicationItem si = new SyndicationItem(titleElement.Value, descriptionElement.Value, new Uri(linkElement.Value));
                            DateTime parsedDateResult;
                            XElement pubDateElement = item.Element("pubDate");
                            if (pubDateElement == null)
                            {
                                parsedDateResult = DateTime.Now;
                            }
                            else
                            {
                                string pubDate = pubDateElement.Value;
                                if (!DateTime.TryParse(pubDate, out parsedDateResult))
                                {
                                    CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
                                    const DateTimeStyles styles = DateTimeStyles.None;
                                    if (!DateTime.TryParse(pubDate, culture, styles, out parsedDateResult))
                                    {
                                        pubDate = pubDate.Replace(" EST", String.Empty);
                                        const string format = "ddd, dd MMM yyyy HH:mm:ss";
                                        if (
                                            !DateTime.TryParseExact(pubDate, format, CultureInfo.InvariantCulture,
                                                DateTimeStyles.None, out parsedDateResult))
                                        {
                                            parsedDateResult = DateTime.Now;
                                        }
                                    }
                                }
                            }
                            si.PublishDate = parsedDateResult;
                            XElement xElement = descriptionElement;
                            si.Summary = new TextSyndicationContent(xElement.Value);
                            ItemWrapper iw = new ItemWrapper(si, this, _feed.Title.Text);
                            Items.Add(iw);
                        }
                        catch (Exception ex)
                        {
                            ehandler.HandleException(ex, url);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Exceptions.Add(ex);
                ehandler.HandleException(ex, url);
            }
        }
        public void RaiseNotifyOnCounts()
        {
            OnPropertyChanged("FeedPublisherName");
            OnPropertyChanged("VisibleCount");
            OnPropertyChanged("FilteredCount");
            OnPropertyChanged("ItemsCount");
        }
        public void PushIncludeInView()
        {
            _copyOfIncludeInViewForRestore = IncludeInView;
        }
        public void PopOldValuesIncludeInView()
        {
            IncludeInView = _copyOfIncludeInViewForRestore;
        }
        public override string ToString()
        {
            return String.Format("{0} | {1} | {2} | {3}", FeedPublisherName, WrappersHaveBeenProcessed, IncludeInView, Url);
        }
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
        #endregion
    }
}